package com.jsgt.greekfire.common.widget

import android.content.Context
import android.graphics.*
import android.util.ArrayMap
import android.view.View
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ItemDecoration
import java.util.regex.Pattern

/**
 * RecycleView item decoration
 * Created by Eminem Lo on 24/11/15.
 * Email arjinmc@hotmail.com
 */
class RecyclerViewItemDecoration : ItemDecoration() {
    /**
     * image resource id for R.java
     */
    private var mDrawableRid = 0

    /**
     * decoration color
     */
    private var mColor =
        Color.parseColor(DEFAULT_COLOR)

    /**
     * decoration thickness
     */
    private var mThickness = 0

    /**
     * decoration dash with
     */
    private var mDashWidth = 0

    /**
     * decoration dash gap
     */
    private var mDashGap = 0
    private var mFirstLineVisible = false
    private var mLastLineVisible = false
    private var mPaddingStart = 0
    private var mPaddingEnd = 0

    /**
     * border line for grid mode
     */
    private var mGridLeftVisible = false
    private var mGridRightVisible = false
    private var mGridTopVisible = false
    private var mGridBottomVisible = false

    /**
     * spacing for grid mode
     */
    var mGridHorizontalSpacing = 0
    var mGridVerticalSpacing = 0

    /***
     * ignore the item types which won't be drew item decoration
     * only for mode horizontal and vertical
     */
    private var mIgnoreTypes: ArrayMap<Int, Int>? = null

    /**
     * direction mode for decoration
     */
    private var mMode = 0
    private var mPaint: Paint? = null
    private var mBmp: Bitmap? = null
    private var mNinePatch: NinePatch? = null

    /**
     * choose the real thickness for image or thickness
     */
    private var mCurrentThickness = 0

    /**
     * sign for if the resource image is a ninepatch image
     */
    private var hasNinePatch = false

    /**
     * sign for if has get the parent RecyclerView LayoutManager mode
     */
    private var hasGetParentLayoutMode = false
    private var mContext: Context? = null
    fun setParams(
        context: Context?,
        params: Param
    ) {
        mContext = context
        mDrawableRid = params.drawableRid
        mColor = params.color
        mThickness = params.thickness
        mDashGap = params.dashGap
        mDashWidth = params.dashWidth
        mPaddingStart = params.paddingStart
        mPaddingEnd = params.paddingEnd
        mFirstLineVisible = params.firstLineVisible
        mLastLineVisible = params.lastLineVisible
        mGridLeftVisible = params.gridLeftVisible
        mGridRightVisible = params.gridRightVisible
        mGridTopVisible = params.gridTopVisible
        mGridBottomVisible = params.gridBottomVisible
        mGridHorizontalSpacing = params.gridHorizontalSpacing
        mGridVerticalSpacing = params.gridVerticalSpacing
        if (params.ignoreTypes != null && params.ignoreTypes!!.size != 0) {
            mIgnoreTypes = ArrayMap()
            val ignoreTypeSize = params.ignoreTypes!!.size
            for (i in 0 until ignoreTypeSize) {
                mIgnoreTypes!![params.ignoreTypes!![i]] = params.ignoreTypes!![i]
            }
        }
    }

    private fun initPaint(context: Context?) {
        mBmp = BitmapFactory.decodeResource(context!!.resources, mDrawableRid)
        if (mBmp != null) {
            if (mBmp!!.ninePatchChunk != null) {
                hasNinePatch = true
                mNinePatch = NinePatch(mBmp, mBmp!!.ninePatchChunk, null)
            }
            if (mMode == RVItemDecorationConst.MODE_HORIZONTAL) mCurrentThickness =
                if (mThickness == 0) mBmp!!.height else mThickness
            if (mMode == RVItemDecorationConst.MODE_VERTICAL) mCurrentThickness =
                if (mThickness == 0) mBmp!!.width else mThickness
        }
        mPaint = Paint()
        mPaint!!.color = mColor
        mPaint!!.style = Paint.Style.STROKE
        mPaint!!.strokeWidth = mThickness.toFloat()
    }

    override fun onDraw(
        c: Canvas,
        parent: RecyclerView,
        state: RecyclerView.State
    ) {
        if (parent.adapter == null || parent.childCount == 0) {
            return
        }
        if (mColor != 0) {
            mPaint!!.color = mColor
            if (mMode == RVItemDecorationConst.MODE_HORIZONTAL) {
                drawHorizontal(c, parent)
            } else if (mMode == RVItemDecorationConst.MODE_VERTICAL) {
                drawVertical(c, parent)
            } else if (mMode == RVItemDecorationConst.MODE_GRID) {
                drawGrid(c, parent)
            }
        }
    }

    override fun getItemOffsets(
        outRect: Rect,
        view: View,
        parent: RecyclerView,
        state: RecyclerView.State
    ) {
        if (!hasGetParentLayoutMode) {
            compatibleWithLayoutManager(parent)
            hasGetParentLayoutMode = true
        }
        val viewPosition = parent.layoutManager!!.getPosition(view)
        if (mMode == RVItemDecorationConst.MODE_HORIZONTAL) {
            if (!isIgnoreType(parent.adapter!!.getItemViewType(viewPosition))) {
                if (!(!mLastLineVisible &&
                            viewPosition == parent.adapter!!.itemCount - 1)
                ) {
                    if (mDrawableRid != 0) {
                        outRect[0, 0, 0] = mCurrentThickness
                    } else {
                        outRect[0, 0, 0] = mThickness
                    }
                }
                if (mFirstLineVisible && viewPosition == 0) {
                    if (mDrawableRid != 0) {
                        outRect[0, mCurrentThickness, 0] = mCurrentThickness
                    } else {
                        outRect[0, mThickness, 0] = mThickness
                    }
                }
            } else {
                outRect[0, 0, 0] = 0
            }
        } else if (mMode == RVItemDecorationConst.MODE_VERTICAL) {
            if (!isIgnoreType(parent.adapter!!.getItemViewType(viewPosition))) {
                if (!(!mLastLineVisible &&
                            viewPosition == parent.adapter!!.itemCount - 1)
                ) {
                    if (mDrawableRid != 0) {
                        outRect[0, 0, mCurrentThickness] = 0
                    } else {
                        outRect[0, 0, mThickness] = 0
                    }
                }
                if (mFirstLineVisible && viewPosition == 0) {
                    if (mDrawableRid != 0) {
                        outRect[mCurrentThickness, 0, mCurrentThickness] = 0
                    } else {
                        outRect[mThickness, 0, mThickness] = 0
                    }
                }
            } else {
                outRect[0, 0, 0] = 0
            }
        } else if (mMode == RVItemDecorationConst.MODE_GRID) {
            val columnSize = (parent.layoutManager as GridLayoutManager?)!!.spanCount
            val itemSize = parent.adapter!!.itemCount
            if (mDrawableRid != 0) {
                if (hasNinePatch) {
                    setGridOffsets(outRect, viewPosition, columnSize, itemSize, 0)
                } else {
                    setGridOffsets(outRect, viewPosition, columnSize, itemSize, 1)
                }
            } else {
                setGridOffsets(outRect, viewPosition, columnSize, itemSize, -1)
            }
        }
    }

    private val isPureLine: Boolean
        private get() = mDashGap == 0 && mDashWidth == 0

    /**
     * draw horizontal decoration
     *
     * @param c
     * @param parent
     */
    private fun drawHorizontal(c: Canvas, parent: RecyclerView) {
        val childrenCount = parent.childCount
        if (mDrawableRid != 0) {
            if (mFirstLineVisible) {
                val childView = parent.getChildAt(0)
                val myY = childView.top
                if (!isIgnoreType(
                        parent.adapter!!.getItemViewType(
                            parent.layoutManager!!.getPosition(childView)
                        )
                    )
                ) {
                    if (hasNinePatch) {
                        val rect = Rect(
                            mPaddingStart, myY - mCurrentThickness
                            , parent.width - mPaddingEnd, myY
                        )
                        mNinePatch!!.draw(c, rect)
                    } else {
                        c.drawBitmap(
                            mBmp!!,
                            mPaddingStart.toFloat(),
                            myY - mCurrentThickness.toFloat(),
                            mPaint
                        )
                    }
                }
            }
            for (i in 0 until childrenCount) {
                if (!mLastLineVisible && i == childrenCount - 1) break
                val childView = parent.getChildAt(i)
                if (!isIgnoreType(
                        parent.adapter!!.getItemViewType(
                            parent.layoutManager!!.getPosition(childView)
                        )
                    )
                ) {
                    val myY = childView.bottom
                    if (hasNinePatch) {
                        val rect = Rect(
                            mPaddingStart, myY
                            , parent.width - mPaddingEnd, myY + mCurrentThickness
                        )
                        mNinePatch!!.draw(c, rect)
                    } else {
                        c.drawBitmap(mBmp!!, mPaddingStart.toFloat(), myY.toFloat(), mPaint)
                    }
                }
            }
        } else {
            val isPureLine = isPureLine
            if (!isPureLine) {
                val effects: PathEffect = DashPathEffect(
                    floatArrayOf(0f, 0f, mDashWidth.toFloat(), mThickness.toFloat()),
                    mDashGap.toFloat()
                )
                mPaint!!.pathEffect = effects
            }
            if (mFirstLineVisible) {
                val childView = parent.getChildAt(0)
                if (!isIgnoreType(
                        parent.adapter!!.getItemViewType(
                            parent.layoutManager!!.getPosition(childView)
                        )
                    )
                ) {
                    val myY = childView.top - mThickness / 2
                    val path = Path()
                    path.moveTo(mPaddingStart.toFloat(), myY.toFloat())
                    path.lineTo(parent.width - mPaddingEnd.toFloat(), myY.toFloat())
                    c.drawPath(path, mPaint!!)
                }
            }
            for (i in 0 until childrenCount) {
                if (!mLastLineVisible && i == childrenCount - 1) break
                val childView = parent.getChildAt(i)
                if (!isIgnoreType(
                        parent.adapter!!.getItemViewType(
                            parent.layoutManager!!.getPosition(childView)
                        )
                    )
                ) {
                    val myY = childView.bottom + mThickness / 2
                    val path = Path()
                    path.moveTo(mPaddingStart.toFloat(), myY.toFloat())
                    path.lineTo(parent.width - mPaddingEnd.toFloat(), myY.toFloat())
                    c.drawPath(path, mPaint!!)
                }
            }
        }
    }

    /**
     * draw vertival decoration
     *
     * @param c
     * @param parent
     */
    private fun drawVertical(c: Canvas, parent: RecyclerView) {
        val childrenCount = parent.childCount
        if (mDrawableRid != 0) {
            if (mFirstLineVisible) {
                val childView = parent.getChildAt(0)
                if (!isIgnoreType(
                        parent.adapter!!.getItemViewType(
                            parent.layoutManager!!.getPosition(childView)
                        )
                    )
                ) {
                    val myX = childView.left
                    if (hasNinePatch) {
                        val rect = Rect(
                            myX - mCurrentThickness, mPaddingStart
                            , myX, parent.height - mPaddingEnd
                        )
                        mNinePatch!!.draw(c, rect)
                    } else {
                        c.drawBitmap(
                            mBmp!!,
                            myX - mCurrentThickness.toFloat(),
                            mPaddingStart.toFloat(),
                            mPaint
                        )
                    }
                }
            }
            for (i in 0 until childrenCount) {
                if (!mLastLineVisible && i == childrenCount - 1) break
                val childView = parent.getChildAt(i)
                if (!isIgnoreType(
                        parent.adapter!!.getItemViewType(
                            parent.layoutManager!!.getPosition(childView)
                        )
                    )
                ) {
                    val myX = childView.right
                    if (hasNinePatch) {
                        val rect = Rect(
                            myX, mPaddingStart, myX + mCurrentThickness
                            , parent.height - mPaddingEnd
                        )
                        mNinePatch!!.draw(c, rect)
                    } else {
                        c.drawBitmap(mBmp!!, myX.toFloat(), mPaddingStart.toFloat(), mPaint)
                    }
                }
            }
        } else {
            val isPureLine = isPureLine
            if (!isPureLine) {
                val effects: PathEffect = DashPathEffect(
                    floatArrayOf(0f, 0f, mDashWidth.toFloat(), mThickness.toFloat()),
                    mDashGap.toFloat()
                )
                mPaint!!.pathEffect = effects
            }
            if (mFirstLineVisible) {
                val childView = parent.getChildAt(0)
                if (!isIgnoreType(
                        parent.adapter!!.getItemViewType(
                            parent.layoutManager!!.getPosition(childView)
                        )
                    )
                ) {
                    val myX = childView.left - mThickness / 2
                    val path = Path()
                    path.moveTo(myX.toFloat(), mPaddingStart.toFloat())
                    path.lineTo(myX.toFloat(), parent.height - mPaddingEnd.toFloat())
                    c.drawPath(path, mPaint!!)
                }
            }
            for (i in 0 until childrenCount) {
                if (!mLastLineVisible && i == childrenCount - 1) break
                val childView = parent.getChildAt(i)
                if (!isIgnoreType(
                        parent.adapter!!.getItemViewType(
                            parent.layoutManager!!.getPosition(childView)
                        )
                    )
                ) {
                    val myX = childView.right + mThickness / 2
                    val path = Path()
                    path.moveTo(myX.toFloat(), mPaddingStart.toFloat())
                    path.lineTo(myX.toFloat(), parent.height - mPaddingEnd.toFloat())
                    c.drawPath(path, mPaint!!)
                }
            }
        }
    }

    /**
     * draw grid decoration
     *
     * @param c
     * @param parent
     */
    private fun drawGrid(c: Canvas, parent: RecyclerView) {
        val childrenCount = parent.childCount
        val columnSize = (parent.layoutManager as GridLayoutManager?)!!.spanCount
        val itemSize = parent.adapter!!.itemCount
        if (mDrawableRid != 0) {
            mPaint!!.strokeWidth = mThickness.toFloat()
            for (i in 0 until childrenCount) {
                val childView = parent.getChildAt(i)
                val myT = childView.top
                val myB = childView.bottom
                val myL = childView.left
                val myR = childView.right
                val viewPosition = parent.layoutManager!!.getPosition(childView)

                //when columnSize/spanCount is One
                if (columnSize == 1) {
                    if (isFirstGridRow(viewPosition, columnSize)) {
                        if (mGridLeftVisible) {
                            if (hasNinePatch) {
                                val rect = Rect(
                                    myL - mThickness
                                    , myT
                                    , myL
                                    , myB
                                )
                                mNinePatch!!.draw(c, rect)
                            } else {
                                c.drawBitmap(
                                    mBmp!!,
                                    myL - mThickness.toFloat(),
                                    myT.toFloat(),
                                    mPaint
                                )
                            }
                        }
                        if (mGridTopVisible) {
                            if (hasNinePatch) {
                                val rect = Rect(
                                    myL - mThickness
                                    , myT - mThickness
                                    , myR + mThickness
                                    , myT
                                )
                                mNinePatch!!.draw(c, rect)
                            } else {
                                c.drawBitmap(
                                    mBmp!!,
                                    myL - mThickness.toFloat(),
                                    myT - mThickness.toFloat(),
                                    mPaint
                                )
                            }
                        }
                        if (mGridRightVisible) {
                            if (hasNinePatch) {
                                val rect = Rect(
                                    myR
                                    , myT
                                    , myR + mThickness
                                    , myB
                                )
                                mNinePatch!!.draw(c, rect)
                            } else {
                                c.drawBitmap(mBmp!!, myR.toFloat(), myT.toFloat(), mPaint)
                            }
                        }

                        //not first row
                    } else {
                        if (mGridLeftVisible) {
                            if (hasNinePatch) {
                                val rect = Rect(
                                    myL - mThickness
                                    , myT - mThickness
                                    , myL
                                    , myB
                                )
                                mNinePatch!!.draw(c, rect)
                            } else {
                                c.drawBitmap(
                                    mBmp!!
                                    , myL - mThickness
                                        .toFloat(), myT - mThickness
                                        .toFloat(), mPaint
                                )
                            }
                        }
                        if (mGridRightVisible) {
                            if (hasNinePatch) {
                                val rect = Rect(
                                    myR
                                    , myT - mThickness
                                    , myR + mThickness
                                    , myB
                                )
                                mNinePatch!!.draw(c, rect)
                            } else {
                                c.drawBitmap(
                                    mBmp!!,
                                    myR.toFloat(),
                                    myT - mThickness.toFloat(),
                                    mPaint
                                )
                            }
                        }
                    }
                    if (isLastGridRow(viewPosition, itemSize, columnSize)) {
                        if (mGridBottomVisible) {
                            if (hasNinePatch) {
                                val rect = Rect(
                                    myL - mThickness
                                    , myB
                                    , myR + mThickness
                                    , myB + mThickness
                                )
                                mNinePatch!!.draw(c, rect)
                            } else {
                                c.drawBitmap(
                                    mBmp!!,
                                    myL - mThickness.toFloat(),
                                    myB.toFloat(),
                                    mPaint
                                )
                            }
                        }
                    } else {
                        if (hasNinePatch) {
                            val rect = Rect(
                                myL
                                , myB
                                , myR + mThickness
                                , myB + mThickness
                            )
                            mNinePatch!!.draw(c, rect)
                        } else {
                            c.drawBitmap(
                                mBmp!!
                                , myL
                                    .toFloat(), myB
                                    .toFloat(), mPaint
                            )
                        }
                    }

                    //when columnSize/spanCount is Not One
                } else {
                    if (isFirstGridColumn(viewPosition, columnSize) && isFirstGridRow(
                            viewPosition,
                            columnSize
                        )
                    ) {
                        if (mGridLeftVisible) {
                            if (hasNinePatch) {
                                val rect = Rect(
                                    myL - mThickness
                                    , myT - mThickness
                                    , myL
                                    , myB
                                )
                                mNinePatch!!.draw(c, rect)
                            } else {
                                c.drawBitmap(
                                    mBmp!!,
                                    myL - mThickness.toFloat(),
                                    myT - mThickness.toFloat(),
                                    mPaint
                                )
                            }
                        }
                        if (mGridTopVisible) {
                            if (hasNinePatch) {
                                val rect = Rect(
                                    myL
                                    , myT - mThickness
                                    , myR
                                    , myT
                                )
                                mNinePatch!!.draw(c, rect)
                            } else {
                                c.drawBitmap(
                                    mBmp!!,
                                    myL.toFloat(),
                                    myT - mThickness.toFloat(),
                                    mPaint
                                )
                            }
                        }
                        if (itemSize == 1) {
                            if (mGridRightVisible) {
                                if (hasNinePatch) {
                                    val rect = Rect(
                                        myR
                                        , myT - mThickness
                                        , myR + mThickness
                                        , myB
                                    )
                                    mNinePatch!!.draw(c, rect)
                                } else {
                                    c.drawBitmap(
                                        mBmp!!,
                                        myR.toFloat(),
                                        myT - mThickness.toFloat(),
                                        mPaint
                                    )
                                }
                            }
                        } else {
                            if (hasNinePatch) {
                                val rect = Rect(
                                    myR
                                    , myT - mThickness
                                    , myR + mThickness
                                    , myB
                                )
                                mNinePatch!!.draw(c, rect)
                            } else {
                                c.drawBitmap(
                                    mBmp!!
                                    , myR
                                        .toFloat(), myT - mThickness
                                        .toFloat(), mPaint
                                )
                            }
                        }
                    } else if (isFirstGridRow(viewPosition, columnSize)) {
                        if (mGridTopVisible) {
                            if (hasNinePatch) {
                                val rect = Rect(
                                    myL
                                    , myT - mThickness
                                    , myR
                                    , myT
                                )
                                mNinePatch!!.draw(c, rect)
                            } else {
                                c.drawBitmap(
                                    mBmp!!,
                                    myL.toFloat(),
                                    myT - mThickness.toFloat(),
                                    mPaint
                                )
                            }
                        }
                        if (isLastGridColumn(viewPosition, itemSize, columnSize)) {
                            if (mGridRightVisible) {
                                if (hasNinePatch) {
                                    val rect = Rect(
                                        myR
                                        , myT - mThickness
                                        , myR + mThickness
                                        , myB
                                    )
                                    mNinePatch!!.draw(c, rect)
                                } else {
                                    c.drawBitmap(
                                        mBmp!!,
                                        myR.toFloat(),
                                        myT - mThickness.toFloat(),
                                        mPaint
                                    )
                                }
                            }
                        } else {
                            if (hasNinePatch) {
                                val rect = Rect(
                                    myR
                                    , myT - mThickness
                                    , myR + mThickness
                                    , myB
                                )
                                mNinePatch!!.draw(c, rect)
                            } else {
                                c.drawBitmap(
                                    mBmp!!
                                    , myR
                                        .toFloat(), myT - mBmp!!.height
                                        .toFloat(), mPaint
                                )
                            }
                        }
                    } else if (isFirstGridColumn(viewPosition, columnSize)) {
                        if (mGridLeftVisible) {
                            if (hasNinePatch) {
                                val rect = Rect(
                                    myL - mThickness
                                    , myT
                                    , myL
                                    , myB
                                )
                                mNinePatch!!.draw(c, rect)
                            } else {
                                c.drawBitmap(
                                    mBmp!!,
                                    myL - mThickness.toFloat(),
                                    myT.toFloat(),
                                    mPaint
                                )
                            }
                        }
                        if (hasNinePatch) {
                            val rect = Rect(
                                myR
                                , myT
                                , myR + mThickness
                                , myB
                            )
                            mNinePatch!!.draw(c, rect)
                        } else {
                            c.drawBitmap(
                                mBmp!!
                                , myR
                                    .toFloat(), myT
                                    .toFloat(), mPaint
                            )
                        }
                    } else {
                        if (isLastGridColumn(viewPosition, itemSize, columnSize)) {
                            if (mGridRightVisible) {
                                if (hasNinePatch) {
                                    val rect = Rect(
                                        myR
                                        , myT - mThickness
                                        , myR + mThickness
                                        , myB
                                    )
                                    mNinePatch!!.draw(c, rect)
                                } else {
                                    c.drawBitmap(
                                        mBmp!!
                                        , myR
                                            .toFloat(), myT - mBmp!!.height
                                            .toFloat(), mPaint
                                    )
                                }
                            }
                        } else {
                            if (hasNinePatch) {
                                val rect = Rect(
                                    myR
                                    , myT
                                    , myR + mThickness
                                    , myB
                                )
                                mNinePatch!!.draw(c, rect)
                            } else {
                                c.drawBitmap(
                                    mBmp!!
                                    , myR
                                        .toFloat(), myT
                                        .toFloat(), mPaint
                                )
                            }
                        }
                    }

                    //bottom line
                    if (isLastGridRow(viewPosition, itemSize, columnSize)) {
                        if (mGridBottomVisible) {
                            if (itemSize == 1) {
                                if (hasNinePatch) {
                                    val rect = Rect(
                                        myL - mThickness
                                        , myB
                                        , myR + if (mGridRightVisible) mThickness else 0
                                        , myB + mThickness
                                    )
                                    mNinePatch!!.draw(c, rect)
                                } else {
                                    c.drawBitmap(
                                        mBmp!!
                                        , myL - mThickness
                                            .toFloat(), myB
                                            .toFloat(), mPaint
                                    )
                                }
                            } else if (isLastGridColumn(viewPosition, itemSize, columnSize)) {
                                if (hasNinePatch) {
                                    val rect = Rect(
                                        myL - mThickness
                                        , myB
                                        , myR + mThickness
                                        , myB + mThickness
                                    )
                                    mNinePatch!!.draw(c, rect)
                                } else {
                                    c.drawBitmap(
                                        mBmp!!
                                        , myL - mThickness
                                            .toFloat(), myB + mThickness / 2
                                            .toFloat(), mPaint
                                    )
                                }
                            } else {
                                if (hasNinePatch) {
                                    val rect = Rect(
                                        myL - mThickness
                                        ,
                                        myB
                                        ,
                                        myR + if (mGridHorizontalSpacing == 0) mThickness else mGridHorizontalSpacing
                                        ,
                                        myB + mThickness
                                    )
                                    mNinePatch!!.draw(c, rect)
                                } else {
                                    c.drawBitmap(
                                        mBmp!!
                                        , myL - mThickness
                                            .toFloat(), myB.toFloat(), mPaint
                                    )
                                }
                            }
                        }
                    } else {
                        if (hasNinePatch) {
                            val rect = Rect(
                                myL - mThickness
                                ,
                                myB
                                ,
                                myR
                                ,
                                myB + if (mGridVerticalSpacing == 0) mThickness else mGridVerticalSpacing
                            )
                            mNinePatch!!.draw(c, rect)
                        } else {
                            c.drawBitmap(
                                mBmp!!
                                , myL - mBmp!!.width
                                    .toFloat(), myB
                                    .toFloat(), mPaint
                            )
                        }
                    }
                }
            }
        } else {
            if (!isPureLine) {
                val effects: PathEffect = DashPathEffect(
                    floatArrayOf(0f, 0f, mDashWidth.toFloat(), mThickness.toFloat()),
                    mDashGap.toFloat()
                )
                mPaint!!.pathEffect = effects
            }
            for (i in 0 until childrenCount) {
                val childView = parent.getChildAt(i)
                val myT = childView.top
                val myB = childView.bottom
                val myL = childView.left
                val myR = childView.right
                val viewPosition = parent.layoutManager!!.getPosition(childView)

                //when columnSize/spanCount is One
                if (columnSize == 1) {
                    if (isFirstGridRow(viewPosition, columnSize)) {
                        if (mGridLeftVisible) {
                            mPaint!!.strokeWidth = mThickness.toFloat()
                            val path = Path()
                            path.moveTo(myL - mThickness / 2.toFloat(), myT.toFloat())
                            path.lineTo(myL - mThickness / 2.toFloat(), myB.toFloat())
                            c.drawPath(path, mPaint!!)
                        }
                        if (mGridTopVisible) {
                            mPaint!!.strokeWidth = mThickness.toFloat()
                            val path = Path()
                            path.moveTo(myL - mThickness.toFloat(), myT - mThickness / 2.toFloat())
                            path.lineTo(myR + mThickness.toFloat(), myT - mThickness / 2.toFloat())
                            c.drawPath(path, mPaint!!)
                        }
                        if (mGridRightVisible) {
                            mPaint!!.strokeWidth = mThickness.toFloat()
                            val path = Path()
                            path.moveTo(myR + mThickness / 2.toFloat(), myT.toFloat())
                            path.lineTo(myR + mThickness / 2.toFloat(), myB.toFloat())
                            c.drawPath(path, mPaint!!)
                        }

                        //not first row
                    } else {
                        if (mGridLeftVisible) {
                            mPaint!!.strokeWidth = mThickness.toFloat()
                            val path = Path()
                            path.moveTo(
                                myL - mThickness / 2
                                    .toFloat(),
                                myT - (if (mGridVerticalSpacing == 0) mThickness else mGridVerticalSpacing).toFloat()
                            )
                            path.lineTo(
                                myL - mThickness / 2
                                    .toFloat(), myB.toFloat()
                            )
                            c.drawPath(path, mPaint!!)
                        }
                        if (mGridRightVisible) {
                            val path = Path()
                            mPaint!!.strokeWidth = mThickness.toFloat()
                            path.moveTo(
                                myR + mThickness / 2
                                    .toFloat(), myT.toFloat()
                            )
                            path.lineTo(
                                myR + mThickness / 2
                                    .toFloat(), myB.toFloat()
                            )
                            c.drawPath(path, mPaint!!)
                        }
                    }
                    if (isLastGridRow(viewPosition, itemSize, columnSize)) {
                        if (mGridBottomVisible) {
                            mPaint!!.strokeWidth = mThickness.toFloat()
                            val path = Path()
                            path.moveTo(
                                myL - mThickness
                                    .toFloat(), myB + mThickness / 2.toFloat()
                            )
                            path.lineTo(
                                myR + mThickness
                                    .toFloat(), myB + mThickness / 2.toFloat()
                            )
                            c.drawPath(path, mPaint!!)
                        }
                    } else {
                        mPaint!!.strokeWidth = mThickness.toFloat()
                        if (mGridVerticalSpacing != 0) {
                            mPaint!!.strokeWidth = mGridVerticalSpacing.toFloat()
                        }
                        val path = Path()
                        path.moveTo(
                            myL
                                .toFloat(),
                            myB + (if (mGridVerticalSpacing == 0) mThickness else mGridVerticalSpacing) / 2.toFloat()
                        )
                        path.lineTo(
                            myR + mThickness
                                .toFloat(),
                            myB + (if (mGridVerticalSpacing == 0) mThickness else mGridVerticalSpacing) / 2.toFloat()
                        )
                        c.drawPath(path, mPaint!!)
                    }

                    //when columnSize/spanCount is Not One
                } else {
                    if (isFirstGridColumn(viewPosition, columnSize) && isFirstGridRow(
                            viewPosition,
                            columnSize
                        )
                    ) {
                        if (mGridLeftVisible) {
                            mPaint!!.strokeWidth = mThickness.toFloat()
                            val path = Path()
                            path.moveTo(
                                myL - mThickness / 2
                                    .toFloat(), myT - mThickness.toFloat()
                            )
                            path.lineTo(
                                myL - mThickness / 2
                                    .toFloat(), myB.toFloat()
                            )
                            c.drawPath(path, mPaint!!)
                        }
                        if (mGridTopVisible) {
                            mPaint!!.strokeWidth = mThickness.toFloat()
                            val path = Path()
                            path.moveTo(
                                myL
                                    .toFloat(), myT - mThickness / 2.toFloat()
                            )
                            path.lineTo(
                                myR
                                    .toFloat(), myT - mThickness / 2.toFloat()
                            )
                            c.drawPath(path, mPaint!!)
                        }
                        if (itemSize == 1) {
                            if (mGridRightVisible) {
                                mPaint!!.strokeWidth = mThickness.toFloat()
                                val path = Path()
                                path.moveTo(
                                    myR + mThickness / 2
                                        .toFloat(), myT - mThickness.toFloat()
                                )
                                path.lineTo(
                                    myR + mThickness / 2
                                        .toFloat(), myB.toFloat()
                                )
                                c.drawPath(path, mPaint!!)
                            }
                        } else {
                            mPaint!!.strokeWidth = mThickness.toFloat()
                            if (mGridHorizontalSpacing != 0) {
                                mPaint!!.strokeWidth = mGridHorizontalSpacing.toFloat()
                            }
                            val path = Path()
                            path.moveTo(
                                myR + (if (mGridHorizontalSpacing == 0) mThickness else mGridHorizontalSpacing) / 2
                                    .toFloat(), myT - mThickness.toFloat()
                            )
                            path.lineTo(
                                myR + (if (mGridHorizontalSpacing == 0) mThickness else mGridHorizontalSpacing) / 2
                                    .toFloat(), myB.toFloat()
                            )
                            c.drawPath(path, mPaint!!)
                        }
                    } else if (isFirstGridRow(viewPosition, columnSize)) {
                        if (mGridTopVisible) {
                            mPaint!!.strokeWidth = mThickness.toFloat()
                            val path = Path()
                            path.moveTo(
                                myL
                                    .toFloat(), myT - mThickness / 2.toFloat()
                            )
                            path.lineTo(
                                myR
                                    .toFloat(), myT - mThickness / 2.toFloat()
                            )
                            c.drawPath(path, mPaint!!)
                        }
                        if (isLastGridColumn(viewPosition, itemSize, columnSize)) {
                            mPaint!!.strokeWidth = mThickness.toFloat()
                            if (mGridRightVisible) {
                                var alterY = 0
                                if (isLastSecondGridRowNotDivided(
                                        viewPosition,
                                        itemSize,
                                        columnSize
                                    )
                                ) {
                                    alterY =
                                        if (mGridVerticalSpacing == 0) mThickness else mGridVerticalSpacing
                                }
                                val path = Path()
                                path.moveTo(
                                    myR + mThickness / 2
                                        .toFloat(), myT - mThickness.toFloat()
                                )
                                path.lineTo(
                                    myR + mThickness / 2
                                        .toFloat(), myB + alterY.toFloat()
                                )
                                c.drawPath(path, mPaint!!)
                            }
                        } else {
                            if (mGridHorizontalSpacing != 0) {
                                mPaint!!.strokeWidth = mGridHorizontalSpacing.toFloat()
                            }
                            val path = Path()
                            path.moveTo(
                                myR + (if (mGridHorizontalSpacing == 0) mThickness else mGridHorizontalSpacing) / 2
                                    .toFloat(), myT - mThickness.toFloat()
                            )
                            path.lineTo(
                                myR + (if (mGridHorizontalSpacing == 0) mThickness else mGridHorizontalSpacing) / 2
                                    .toFloat(), myB.toFloat()
                            )
                            c.drawPath(path, mPaint!!)
                        }
                    } else if (isFirstGridColumn(viewPosition, columnSize)) {
                        if (mGridLeftVisible) {
                            mPaint!!.strokeWidth = mThickness.toFloat()
                            val path = Path()
                            path.moveTo(
                                myL - mThickness / 2
                                    .toFloat(), myT.toFloat()
                            )
                            path.lineTo(
                                myL - mThickness / 2
                                    .toFloat(), myB.toFloat()
                            )
                            c.drawPath(path, mPaint!!)
                        }
                        mPaint!!.strokeWidth = mThickness.toFloat()
                        if (mGridHorizontalSpacing != 0) {
                            mPaint!!.strokeWidth = mGridHorizontalSpacing.toFloat()
                        }
                        val path = Path()
                        path.moveTo(
                            myR + (if (mGridHorizontalSpacing == 0) mThickness else mGridHorizontalSpacing) / 2
                                .toFloat(), myT.toFloat()
                        )
                        path.lineTo(
                            myR + (if (mGridHorizontalSpacing == 0) mThickness else mGridHorizontalSpacing) / 2
                                .toFloat(), myB.toFloat()
                        )
                        c.drawPath(path, mPaint!!)
                    } else {
                        mPaint!!.strokeWidth = mThickness.toFloat()
                        if (isLastGridColumn(viewPosition, itemSize, columnSize)) {
                            if (mGridRightVisible) {
                                var alterY = 0
                                if (isLastSecondGridRowNotDivided(
                                        viewPosition,
                                        itemSize,
                                        columnSize
                                    )
                                ) {
                                    alterY =
                                        if (mGridVerticalSpacing == 0) mThickness else mGridVerticalSpacing
                                }
                                val path = Path()
                                path.moveTo(
                                    myR + mThickness / 2
                                        .toFloat(),
                                    myT - (if (mGridVerticalSpacing == 0) mThickness else mGridVerticalSpacing).toFloat()
                                )
                                path.lineTo(
                                    myR + mThickness / 2
                                        .toFloat(), myB + alterY.toFloat()
                                )
                                c.drawPath(path, mPaint!!)
                            }
                        } else {
                            if (mGridHorizontalSpacing != 0) {
                                mPaint!!.strokeWidth = mGridHorizontalSpacing.toFloat()
                            }
                            val path = Path()
                            path.moveTo(
                                myR + (if (mGridHorizontalSpacing == 0) mThickness else mGridHorizontalSpacing) / 2
                                    .toFloat(), myT.toFloat()
                            )
                            path.lineTo(
                                myR + (if (mGridHorizontalSpacing == 0) mThickness else mGridHorizontalSpacing) / 2
                                    .toFloat(), myB.toFloat()
                            )
                            c.drawPath(path, mPaint!!)
                        }
                    }

                    //bottom line
                    if (isLastGridRow(viewPosition, itemSize, columnSize)) {
                        if (mGridBottomVisible) {
                            mPaint!!.strokeWidth = mThickness.toFloat()
                            if (itemSize == 1) {
                                val path = Path()
                                path.moveTo(
                                    myL - mThickness
                                        .toFloat(), myB + mThickness / 2.toFloat()
                                )
                                path.lineTo(
                                    myR + (if (mGridRightVisible) mThickness else 0)
                                        .toFloat(), myB + mThickness / 2.toFloat()
                                )
                                c.drawPath(path, mPaint!!)
                            } else if (isLastGridColumn(viewPosition, itemSize, columnSize)) {
                                val path = Path()
                                path.moveTo(
                                    myL - (if (mGridHorizontalSpacing == 0) mThickness else mGridHorizontalSpacing)
                                        .toFloat(), myB + mThickness / 2.toFloat()
                                )
                                path.lineTo(
                                    myR + mThickness
                                        .toFloat(), myB + mThickness / 2.toFloat()
                                )
                                c.drawPath(path, mPaint!!)
                            } else {
                                val path = Path()
                                path.moveTo(
                                    myL - (if (mGridHorizontalSpacing == 0) mThickness else mGridHorizontalSpacing)
                                        .toFloat(), myB + mThickness / 2.toFloat()
                                )
                                path.lineTo(
                                    myR + (if (mGridHorizontalSpacing == 0) mThickness else mGridHorizontalSpacing)
                                        .toFloat(), myB + mThickness / 2.toFloat()
                                )
                                c.drawPath(path, mPaint!!)
                            }
                        }
                    } else {
                        mPaint!!.strokeWidth = mThickness.toFloat()
                        if (mGridVerticalSpacing != 0) {
                            mPaint!!.strokeWidth = mGridVerticalSpacing.toFloat()
                        }
                        val path = Path()
                        path.moveTo(
                            myL - (if (mGridHorizontalSpacing == 0) mThickness else mGridHorizontalSpacing)
                                .toFloat(),
                            myB + (if (mGridVerticalSpacing == 0) mThickness else mGridVerticalSpacing) / 2.toFloat()
                        )
                        path.lineTo(
                            myR
                                .toFloat(),
                            myB + (if (mGridVerticalSpacing == 0) mThickness else mGridVerticalSpacing) / 2.toFloat()
                        )
                        c.drawPath(path, mPaint!!)
                    }
                }
            }
        }
    }

    /**
     * set offsets for grid mode
     *
     * @param outRect
     * @param viewPosition
     * @param columnSize
     * @param itemSize
     * @param tag          0 for ninepatch,1 for drawable bitmap
     */
    fun setGridOffsets(
        outRect: Rect, viewPosition: Int, columnSize: Int
        , itemSize: Int, tag: Int
    ) {
        val x: Int
        val y: Int
        val borderThickness = mThickness
        if (tag == 0) {
            y = mThickness
            x = y
            mGridHorizontalSpacing = 0
            mGridVerticalSpacing = mGridHorizontalSpacing
        } else if (tag == 1) {
            x = mBmp!!.width
            y = mBmp!!.height
            mGridHorizontalSpacing = 0
            mGridVerticalSpacing = mGridHorizontalSpacing
        } else {
            x = if (mGridHorizontalSpacing != 0) mGridHorizontalSpacing else mThickness
            y = if (mGridVerticalSpacing != 0) mGridVerticalSpacing else mThickness
        }

        //when columnSize/spanCount is One
        if (columnSize == 1) {
            if (isFirstGridRow(viewPosition, columnSize)) {
                if (isLastGridRow(viewPosition, itemSize, columnSize)) {
                    outRect[if (mGridLeftVisible) borderThickness else 0, if (mGridTopVisible) borderThickness else 0, if (mGridRightVisible) borderThickness else 0] =
                        if (mGridBottomVisible) borderThickness else y
                } else {
                    outRect[if (mGridLeftVisible) borderThickness else 0, if (mGridTopVisible) borderThickness else 0, if (mGridRightVisible) borderThickness else 0] =
                        y
                }
            } else {
                if (isLastGridRow(viewPosition, itemSize, columnSize)) {
                    outRect[if (mGridLeftVisible) borderThickness else 0, 0, if (mGridRightVisible) borderThickness else 0] =
                        if (mGridBottomVisible) borderThickness else 0
                } else {
                    outRect[if (mGridLeftVisible) borderThickness else 0, 0, if (mGridRightVisible) borderThickness else 0] =
                        y
                }
            }
        } else {

            // A
            if (isFirstGridColumn(viewPosition, columnSize)
                && isFirstGridRow(viewPosition, columnSize)
            ) {
                outRect[if (mGridLeftVisible) borderThickness else 0, if (mGridTopVisible) borderThickness else 0, if (itemSize == 1) borderThickness else x / 2] =
                    if (isLastGridRow(
                            viewPosition,
                            itemSize,
                            columnSize
                        )
                    ) borderThickness else y / 2
                // B
            } else if (isFirstGridRow(viewPosition, columnSize)
                && isLastGridColumn(viewPosition, itemSize, columnSize)
            ) {
                outRect[x / 2, if (mGridTopVisible) borderThickness else 0, if (mGridRightVisible) borderThickness else 0] =
                    if (isLastGridRow(
                            viewPosition,
                            itemSize,
                            columnSize
                        )
                    ) borderThickness else y / 2

                // C
            } else if (isLastGridColumn(viewPosition, itemSize, columnSize)
                && isLastGridRow(viewPosition, itemSize, columnSize)
            ) {
                outRect[x / 2, y / 2, if (mGridRightVisible) borderThickness else 0] =
                    if (mGridBottomVisible) borderThickness else 0
                // D
            } else if (isFirstGridColumn(viewPosition, columnSize)
                && isLastGridRow(viewPosition, itemSize, columnSize)
            ) {
                outRect[if (mGridLeftVisible) borderThickness else 0, y / 2, if (isLastGridColumn(
                        viewPosition,
                        itemSize,
                        columnSize
                    )
                ) borderThickness else x / 2] = if (mGridBottomVisible) borderThickness else 0
                // E
            } else if (isFirstGridColumn(viewPosition, columnSize)) {
                outRect[if (mGridLeftVisible) borderThickness else 0, y / 2, if (isLastGridColumn(
                        viewPosition,
                        itemSize,
                        columnSize
                    )
                ) borderThickness else x / 2] = y / 2

                // F
            } else if (isFirstGridRow(viewPosition, columnSize)) {
                outRect[x / 2, if (mGridTopVisible) borderThickness else 0, x / 2] =
                    if (isLastGridRow(
                            viewPosition,
                            itemSize,
                            columnSize
                        )
                    ) borderThickness else y / 2
                // G
            } else if (isLastGridColumn(viewPosition, itemSize, columnSize)) {
                outRect[x / 2, y / 2, if (mGridRightVisible) borderThickness else 0] = y / 2
                // H
            } else if (isLastGridRow(viewPosition, itemSize, columnSize)) {
                outRect[x / 2, y / 2, x / 2] = if (mGridBottomVisible) borderThickness else 0
                // I
            } else {
                outRect[x / 2, y / 2, x / 2] = y / 2
            }
        }
    }

    /**
     * check if is one of the first columns
     *
     * @param position
     * @param columnSize
     * @return
     */
    private fun isFirstGridColumn(position: Int, columnSize: Int): Boolean {
        return position % columnSize == 0
    }

    /**
     * check if is one of the last columns
     *
     * @param position
     * @param columnSize
     * @return
     */
    private fun isLastGridColumn(position: Int, itemSize: Int, columnSize: Int): Boolean {
        var isLast = false
        if ((position + 1) % columnSize == 0) {
            isLast = true
        }
        return isLast
    }

    /**
     * check if is the first row of th grid
     *
     * @param position
     * @param columnSize
     * @return
     */
    private fun isFirstGridRow(position: Int, columnSize: Int): Boolean {
        return position < columnSize
    }

    /**
     * check if is the last row of the grid
     *
     * @param position
     * @param itemSize
     * @param columnSize
     * @return
     */
    private fun isLastGridRow(position: Int, itemSize: Int, columnSize: Int): Boolean {
        val temp = itemSize % columnSize
        if (temp == 0 && position >= itemSize - columnSize) {
            return true
        } else if (position >= itemSize / columnSize * columnSize) {
            return true
        }
        return false
    }

    /**
     * check if is the last second row of the grid when the itemSize cannot be divided by columnSize
     *
     * @param position
     * @param itemSize
     * @param columnSize
     * @return
     */
    private fun isLastSecondGridRowNotDivided(
        position: Int,
        itemSize: Int,
        columnSize: Int
    ): Boolean {
        val temp = itemSize % columnSize
        return temp != 0 && itemSize - 1 - temp == position
    }

    /**
     * compatible with recyclerview layoutmanager
     *
     * @param parent
     */
    private fun compatibleWithLayoutManager(parent: RecyclerView) {
        if (parent.layoutManager != null) {
            if (parent.layoutManager is GridLayoutManager) {
                mMode = RVItemDecorationConst.MODE_GRID
            } else if (parent.layoutManager is LinearLayoutManager) {
                mMode =
                    if ((parent.layoutManager as LinearLayoutManager?)!!.orientation == LinearLayoutManager.HORIZONTAL) {
                        RVItemDecorationConst.MODE_VERTICAL
                    } else {
                        RVItemDecorationConst.MODE_HORIZONTAL
                    }
            }
        } else {
            mMode = RVItemDecorationConst.MODE_UNKNOWN
        }
        initPaint(mContext)
    }

    /**
     * check current item is ignore type
     *
     * @return
     */
    private fun isIgnoreType(viewType: Int): Boolean {
        if (mIgnoreTypes == null || mIgnoreTypes!!.isEmpty()) {
            return false
        }
        return mIgnoreTypes!!.containsKey(viewType)
    }

    class Builder(context: Context) {
        private val params: Param
        private val context: Context
        fun create(): RecyclerViewItemDecoration {
            val recyclerViewItemDecoration =
                RecyclerViewItemDecoration()
            recyclerViewItemDecoration.setParams(context, params)
            return recyclerViewItemDecoration
        }

        fun drawableID(@DrawableRes drawableID: Int): Builder {
            params.drawableRid = drawableID
            return this
        }

        fun color(@ColorInt color: Int): Builder {
            params.color = color
            return this
        }

        fun color(color: String?): Builder {
            if (isColorString(color)) {
                params.color = Color.parseColor(color)
            }
            return this
        }

        //对于GridLayoutManager这个是上下左右的边厚度，对于LinearLayoutManager这个是分隔线高度
        fun thickness(thickness: Int): Builder {
            var thickness = thickness
            if (thickness % 2 != 0) {
                thickness += 1
            }
            if (thickness <= 2) {
                thickness = 2
            }
            params.thickness = thickness
            return this
        }

        fun dashWidth(dashWidth: Int): Builder {
            var dashWidth = dashWidth
            if (dashWidth < 0) {
                dashWidth = 0
            }
            params.dashWidth = dashWidth
            return this
        }

        fun dashGap(dashGap: Int): Builder {
            var dashGap = dashGap
            if (dashGap < 0) {
                dashGap = 0
            }
            params.dashGap = dashGap
            return this
        }

        fun lastLineVisible(visible: Boolean): Builder {
            params.lastLineVisible = visible
            return this
        }

        fun firstLineVisible(visible: Boolean): Builder {
            params.firstLineVisible = visible
            return this
        }

        fun paddingStart(padding: Int): Builder {
            var padding = padding
            if (padding < 0) {
                padding = 0
            }
            params.paddingStart = padding
            return this
        }

        fun paddingEnd(padding: Int): Builder {
            var padding = padding
            if (padding < 0) {
                padding = 0
            }
            params.paddingEnd = padding
            return this
        }

        fun gridLeftVisible(visible: Boolean): Builder {
            params.gridLeftVisible = visible
            return this
        }

        fun gridRightVisible(visible: Boolean): Builder {
            params.gridRightVisible = visible
            return this
        }

        fun gridTopVisible(visible: Boolean): Builder {
            params.gridTopVisible = visible
            return this
        }

        fun gridBottomVisible(visible: Boolean): Builder {
            params.gridBottomVisible = visible
            return this
        }

        fun gridHorizontalSpacing(spacing: Int): Builder {
            var spacing = spacing
            if (spacing < 0) {
                spacing = 0
            }
            if (spacing % 2 != 0) {
                spacing += 1
            }
            params.gridHorizontalSpacing = spacing
            return this
        }

        fun gridVerticalSpacing(spacing: Int): Builder {
            var spacing = spacing
            if (spacing < 0) {
                spacing = 0
            }
            if (spacing % 2 != 0) {
                spacing += 1
            }
            params.gridVerticalSpacing = spacing
            return this
        }

        fun ignoreTypes(ignoreTypes: IntArray?): Builder {
            params.ignoreTypes = ignoreTypes
            return this
        }

        init {
            params = Param()
            this.context = context
        }
    }

    class Param {
        var drawableRid = 0
        var color =
            Color.parseColor(DEFAULT_COLOR)
        var thickness = 0 //对于GridLayoutManager这个是上下左右的边厚度，对于LinearLayoutManager这个是分隔线高度 = 0
        var dashWidth = 0
        var dashGap = 0
        var lastLineVisible = false
        var firstLineVisible = false
        var paddingStart = 0
        var paddingEnd = 0
        var gridLeftVisible = false
        var gridRightVisible = false
        var gridTopVisible = false
        var gridBottomVisible = false
        var gridHorizontalSpacing = 0
        var gridVerticalSpacing = 0
        var ignoreTypes: IntArray? = null
    }

    companion object {
        /**
         * default decoration color
         */
        private const val DEFAULT_COLOR = "#bdbdbd"

        /**
         * judge is a color string like #xxxxxx or #xxxxxxxx
         *
         * @param colorStr
         * @return
         */
        fun isColorString(colorStr: String?): Boolean {
            return Pattern.matches("^#([0-9a-fA-F]{6}||[0-9a-fA-F]{8})$", colorStr)
        }
    }
}